package org.glandais.gtalk.statuscounter;

import java.text.DecimalFormat;
import java.text.NumberFormat;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.lang.StringEscapeUtils;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Presence;
import org.joda.time.DateTime;
import org.joda.time.format.ISODateTimeFormat;

public class App {

	public static void main(String[] args) throws Exception {
		App app = new App();
		boolean parametersOk;
		Options options = app.getOptions();
		try {
			parametersOk = app.getParameters(args, options);
		} catch (Exception e) {
			parametersOk = false;
			e.printStackTrace();
			app.printHelp(options);
		}
		if (parametersOk) {
			app.process();
		}
	}

	public static enum Type {
		minutes, hours, days, weeks;
	}

	private static final NumberFormat TWO_DIGITS = new DecimalFormat("+0.00;-0.00");
	private static final NumberFormat ONE_DIGIT = new DecimalFormat("+0.0;-0.0");
	private static final NumberFormat ZERO_DIGIT = new DecimalFormat("+0;-0");

	private String status;
	private String username;
	private String password;
	private DateTime dateTime;
	private Type type;
	private String prefix;
	private int decimals;

	private void process() throws Exception {
		DateTime now = new DateTime();
		double diff = now.getMillis() - dateTime.getMillis();

		double seconds = diff / 1000.0;
		double minutes = seconds / 60.0;
		double hours = minutes / 60.0;
		double days = hours / 24.0;
		double weeks = days / 7.0;

		switch (type) {
		case minutes:
			diff = minutes;
			break;
		case hours:
			diff = hours;
			break;
		case days:
			diff = days;
			break;
		case weeks:
			diff = weeks;
			break;
		default:
			diff = seconds;
			break;
		}

		String sdiff = "";
		if (decimals == 0) {
			sdiff = ZERO_DIGIT.format(diff);
		}
		if (decimals == 1) {
			sdiff = ONE_DIGIT.format(diff);
		}
		if (decimals == 2) {
			sdiff = TWO_DIGITS.format(diff);
		}

		String status = this.status + " " + prefix + sdiff;

		setStatus(status);
	}

	private boolean getParameters(String[] args, Options options) throws Exception {
		CommandLineParser parser = new PosixParser();

		// parse the command line arguments
		CommandLine line = null;
		try {
			line = parser.parse(options, args);
		} catch (ParseException e) {
			printHelp(options);
			return false;
		}

		if (line.hasOption("help")) {
			printHelp(options);
			return false;
		}

		status = null;
		if (line.hasOption("status")) {
			status = line.getOptionValue("status");
		}
		username = line.getOptionValue("username");
		password = line.getOptionValue("password");
		String datetimeString = line.getOptionValue("datetime");
		dateTime = ISODateTimeFormat.dateTimeNoMillis().parseDateTime(datetimeString);
		String typeString = line.getOptionValue("type");
		type = null;
		if (typeString.equals("minutes")) {
			type = Type.minutes;
			prefix = "m";
			decimals = 0;
		}
		if (typeString.equals("hours")) {
			type = Type.hours;
			prefix = "H";
			decimals = 0;
		}
		if (typeString.equals("days")) {
			type = Type.days;
			prefix = "J";
			decimals = 0;
		}
		if (typeString.equals("weeks")) {
			type = Type.weeks;
			prefix = "S";
			decimals = 1;
		}
		if (line.hasOption("prefix")) {
			prefix = line.getOptionValue("prefix");
		}
		if (line.hasOption("decimals")) {
			decimals = Integer.parseInt(line.getOptionValue("decimals"));
			if (decimals < 0 || decimals > 2) {
				printHelp(options);
				return false;
			}
		}
		return true;
	}

	private Options getOptions() {
		Options options = new Options();
		options.addOption(new Option("help", "print this message"));
		options.addOption(new Option("status", true, "Status prefix"));
		Option usernameOption = new Option("username", true, "Google username WITH @gmail.com");
		usernameOption.setRequired(true);
		options.addOption(usernameOption);
		Option passwordOption = new Option("password", true, "Google password");
		passwordOption.setRequired(true);
		options.addOption(passwordOption);
		Option datetimeOption = new Option("datetime", true, "datetime in ISO8601 format ("
				+ ISODateTimeFormat.dateTimeNoMillis().print(System.currentTimeMillis()) + ")");
		datetimeOption.setRequired(true);
		options.addOption(datetimeOption);
		Option typeOption = new Option("type", true, "minutes, hours, days, weeks");
		typeOption.setRequired(true);
		options.addOption(typeOption);
		options.addOption(new Option("prefix", true, "M, H, D, W"));
		options.addOption(new Option("decimals", true, "0, 1, 2"));
		return options;
	}

	private void setStatus(final String status) throws Exception {
		ConnectionConfiguration cc = new ConnectionConfiguration("talk.google.com", 5222, "gmail.com");
		cc.setSASLAuthenticationEnabled(false);
		XMPPConnection connection = new XMPPConnection(cc);

		connection.connect();
		// SASLAuthentication.supportSASLMechanism("PLAIN", 0);
		connection.login(username, password);

		connection.sendPacket(new IQ() {
			@Override
			public String getChildElementXML() {
				return "<query xmlns='http://jabber.org/protocol/disco#info'/>";
			}

			@Override
			public String getTo() {
				return "gmail.com";
			}

			@Override
			public Type getType() {
				return Type.GET;
			}

		});
		Thread.sleep(2000);

		connection.sendPacket(new IQ() {
			@Override
			public String getChildElementXML() {
				return "<query xmlns='google:shared-status' version='2'/>";
			}

			@Override
			public String getTo() {
				return username;
			}

			@Override
			public Type getType() {
				return Type.GET;
			}

		});
		Thread.sleep(2000);

		Presence presence = new Presence(Presence.Type.available, status, 24, Presence.Mode.available);
		connection.sendPacket(presence);

		connection.sendPacket(new IQ() {

			@Override
			public String getChildElementXML() {
				return "<query xmlns='google:shared-status' version='2'><status>" + StringEscapeUtils.escapeXml(status)
						+ "</status><invisible value='false'/></query>";
			}

			@Override
			public String getTo() {
				return username;
			}

			@Override
			public Type getType() {
				return Type.SET;
			}
		});

		Thread.sleep(2000);
		connection.disconnect();

	}

	private void printHelp(Options options) {
		// automatically generate the help statement
		HelpFormatter formatter = new HelpFormatter();
		formatter.printHelp("status-counter", options);
	}

}
